--[[
模块名称：protoair
模块功能：根据mqtt协议进行封包
模块最后修改时间：2017.08.25
]]
local lpack = require"pack"
local protobuf = require"protobuf"
module(...,package.seeall)

local slen,sbyte,ssub,sgsub,schar,srep,smatch,sgmatch = string.len,string.byte,string.sub,string.gsub,string.char,string.rep,string.match,string.gmatch

GPS,LBS1,LBS2,HEART,LBS3,GPSLBS,GPSLBS1,GPSLBSWIFIEXT,GPSLBSWIFI,RPTPARA,SETPARARSP,DEVEVENTRSP,GPSLBSWIFI1,INFO = 1,2,3,4,5,6,7,8,9,10,11,12,13,14
RPTFREQ,ALMFREQ,GUARDON,GUARDOFF,HORNOFF,HORNON,RELNUM,CALLVOL,CALLRINGVOL,CALLRINGMUTEON,CALLRINGMUTEOFF,AUTOGUARDON,AUTOGUARDOFF,AUTOSLEEPON,AUTOSLEEPOFF,LOCRPON,LOCRPOFF = 3,4,5,6,13,14,15,16,17,18,19,48,49,50,51,52,53
SILTIME,FIXTM,WHITENUM,FIXMOD,SMSFORBIDON,SMSFORBIDOFF,CALLRINGID,SOSIND,ENTERTAIN,ALARM = 20,21,22,23,24,25,26,27,28,29
SETPARA,SENDSMS,DIAL,QRYLOC,RESET,MONIT,POWEROFF,RESTORE,PROMPT,BTCTL,SMPSET,QRYPARA = 10,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x3A,0x3C,0x50
VER = 0
FIXTIMEPOS,QRYPOS = 0,1
BTCLS,BTOPN,BTOTA,BTUPD = 0,1,2,3
DATETIME,poweron = 0,true
local histsnd,timed = {},0

local PROTOVERSION = 0
local serial = 0
local imei -- 命令头包含IMEI,在第一次获取以后做缓存
local get

local function print(...)
	_G.print("protoair",...)
end

function bcd(d,n)
	local l = slen(d or "")
	local num
	local t = {}

	for i=1,l,2 do
		num = tonumber(ssub(d,i,i+1),16)

		if i == l then
			num = 0xf0+num
		else
			num = (num%0x10)*0x10 + num/0x10
		end

		table.insert(t,num)
	end

	local s = schar(_G.unpack(t))

	l = slen(s)

	if l < n then
		s = s .. srep("\255",n-l)
	elseif l > n then
		s = ssub(s,1,n)
	end

	return s
end

local function unbcd(d)
	local byte,v1,v2
	local t = {}

	for i=1,slen(d) do
		byte = sbyte(d,i)
		v1,v2 = bit.band(byte,0x0f),bit.band(bit.rshift(byte,4),0x0f)

		if v1 == 0x0f then break end
		table.insert(t,v1)

		if v2 == 0x0f then break end
		table.insert(t,v2)
	end

	return table.concat(t)
end

local function enlnla(v,s)
	if not v then return string.toHex("FFFFFFFFFF") end
	
	local v1,v2 = smatch(s,"(%d+)%.(%d+)")
	if not v1 or not v2 then return string.toHex("FFFFFFFFFF") end

	if slen(v1) < 3 then v1 = srep("0",3-slen(v1)) .. v1 end

	return bcd(v1..v2,5)
end

-- 基本状态信息封包处理
local function enstat()
	local stat = get("STATUS")
	local rssi = get("RSSI")
	local gpstat = get("GPSTAT")
	local satenum = gpstat.satenum
	local chgstat = chg.getcharger()
	local pwoffcause = 0 --linkair.getpwoffcause()
	local _,adc = adcv.read()

	-- 状态字节1
	local n1 = stat.shake + stat.charger*2 + stat.acc*4 + stat.gps*8 + stat.sleep*16+stat.fly*32+stat.poweroff*64+stat.rest*128
	-- 状态字节2
	rssi = rssi > 31 and 31 or rssi
	satenum = satenum > 7 and 7 or satenum
	local n2 = rssi + satenum*32
	
	local base = lpack.pack(">bbH",n1,n2,stat.volt)
	local extend
	
	extend = lpack.pack(">bHI",0x04,4,os.time())
	
	if get("GPSLEEP") then
		extend = extend..lpack.pack(">bHb",5,1,0)
	else
		extend = extend..lpack.pack(">bHb",5,1,1)	
	end
	
	if chgstat == false then
		extend = extend..lpack.pack(">bHb",6,1,0)
	elseif chgstat == true then
		extend = extend..lpack.pack(">bHb",6,1,1)
	end
	
	if pwoffcause == 0 then
		extend = extend..lpack.pack(">bHb",7,1,0)
	elseif pwoffcause == 1 then
		extend = extend..lpack.pack(">bHb",7,1,1)
	end
	
	if timed == 0 then
		extend = extend..lpack.pack(">bHH",0x0B,2,0)
	else
		extend = extend..lpack.pack(">bHH",0x0B,2,timed)	
	end
		
	if adc then extend = extend..lpack.pack(">bHH",0x0C,2,adc) end
	
	
	return base..extend
end

local function encellinfo()
	local info,ret,t,lac,ci,rssi,k,v,m,n,cntrssi = get("CELLINFO"),"",{}
	print("encellinfo",info)
	for lac,ci,rssi in sgmatch(info,"(%d+)%.(%d+)%.(%d+);") do
		lac,ci,rssi = tonumber(lac),tonumber(ci),(tonumber(rssi) > 31) and 31 or tonumber(rssi)
		local handle = nil
		for k,v in pairs(t) do
			if v.lac == lac then
				if #v.rssici < 8 then
					table.insert(v.rssici,{rssi=rssi,ci=ci})
				end
				handle = true
				break
			end
		end
		if not handle then
			table.insert(t,{lac=lac,rssici={{rssi=rssi,ci=ci}}})
		end
	end
	for k,v in pairs(t) do
		ret = ret .. lpack.pack(">H",v.lac)
		for m,n in pairs(v.rssici) do
			cntrssi = bit.bor(bit.lshift(((m == 1) and (#v.rssici-1) or 0),5),n.rssi)
			ret = ret .. lpack.pack(">bH",cntrssi,n.ci)
		end
	end

	return schar(#t)..ret
end

local function cellinfoext()
	local info,ret,t,mcc,mnc,lac,ci,rssi,k,v,m,n,cntrssi = get("CELLINFOEXT"),"",{}
	print("cellinfoext",info)
	for mcc,mnc,lac,ci,rssi in sgmatch(info,"(%d+)%.(%d+)%.(%d+)%.(%d+)%.(%d+);") do
		mcc,mnc,lac,ci,rssi = tonumber(mcc),tonumber(mnc),tonumber(lac),tonumber(ci),(tonumber(rssi) > 31) and 31 or tonumber(rssi)
		local handle = nil
		for k,v in pairs(t) do
			if v.lac_id == lac then
				if #v.cells < 8 then
					table.insert(v.cells,{mcc=mcc,mnc=mnc,cell_id=ci,cell_rssi=rssi})
				end
				handle = true
				break
			end
		end
		if not handle then
			table.insert(t,{lac_id=lac,cells={{mcc=mcc,mnc=mnc,cell_id=ci,cell_rssi=rssi}}})
		end
	end
	--[[for k,v in pairs(t) do
		print("cells k,v",k,v.lac_id,#v.cells)
		for k,v in pairs(v.cells) do
			print("cells.cells",v.mcc,v.mnc,v.cell_id,v.cell_rssi)
		end
	end]]
	
	return t
end

local function encellinfoext()
	local info,ret,t,mcc,mnc,lac,ci,rssi,k,v,m,n,cntrssi = get("CELLINFOEXT"),"",{}
	print("encellinfoext",info)
	for mcc,mnc,lac,ci,rssi in sgmatch(info,"(%d+)%.(%d+)%.(%d+)%.(%d+)%.(%d+);") do
		mcc,mnc,lac,ci,rssi = tonumber(mcc),tonumber(mnc),tonumber(lac),tonumber(ci),(tonumber(rssi) > 31) and 31 or tonumber(rssi)
		local handle = nil
		for k,v in pairs(t) do
			if v.lac == lac and v.mcc == mcc and v.mnc == mnc then
				if #v.rssici < 8 then
					table.insert(v.rssici,{rssi=rssi,ci=ci})
				end
				handle = true
				break
			end
		end
		if not handle then
			table.insert(t,{mcc=mcc,mnc=mnc,lac=lac,rssici={{rssi=rssi,ci=ci}}})
		end
	end
	for k,v in pairs(t) do
		ret = ret .. lpack.pack(">HHb",v.lac,v.mcc,v.mnc)
		for m,n in pairs(v.rssici) do
			cntrssi = bit.bor(bit.lshift(((m == 1) and (#v.rssici-1) or 0),5),n.rssi)
			ret = ret .. lpack.pack(">bH",cntrssi,n.ci)
		end
	end

	return schar(#t)..ret
end

--注册proto描述文件
local pbFile = io.open("/ldata/tracker.pb","rb")
local pbBuf = pbFile:read("*a")
pbFile:close()
protobuf.register(pbBuf)

local function decodeAndPrint(encodeStream)
	--使用protobuf.decode反序列化
	--如果成功，反序列化后的数据以table类型赋值给decodeTable
	--如果失败，返回false
	local decodeTable = protobuf.decode("tracker.Client",encodeStream)
	print("decodeTable",decodeTable)
	if decodeTable then
		print("decode message_id",decodeTable.message_id,type(decodeTable.message_id))
		if decodeTable.message_id=="DEV_LOGIN" then
			print("decode project_id",decodeTable.log_in.project_id)
			print("decode project_name",decodeTable.log_in.project_name)
			print("decode script_version",decodeTable.log_in.script_version)
			print("decode iccid",decodeTable.log_in.iccid)
			print("decode imsi",decodeTable.log_in.imsi)
			print("decode heart_interval",decodeTable.log_in.heart_interval)
			print("decode power_on",decodeTable.log_in.power_on)
		elseif decodeTable.message_id=="DEV_HEART" then
			print("decode shake",decodeTable.heart.status.shake)
			print("decode charger",decodeTable.heart.status.charger)
			print("decode acc",decodeTable.heart.status.acc)
			print("decode gps",decodeTable.heart.status.gps)
			print("decode rssi",decodeTable.heart.status.rssi)
			print("decode vbat",decodeTable.heart.status.vbat)
			print("decode charge_status",decodeTable.heart.status.charge_status)
			print("decode adc",decodeTable.heart.status.adc)
		elseif decodeTable.message_id=="DEV_PARA_REPORT" then
			print("decode pararpt type",decodeTable.para_report.type)
			print("decode para guard",decodeTable.para_report.guard)
		elseif decodeTable.message_id=="DEV_SVRSETPARA_RSP" then	
			print("decode set_para_rsp type",decodeTable.set_para_rsp.type)
			print("decode set_para_rsp result",decodeTable.set_para_rsp.result)
		elseif decodeTable.message_id=="DEV_SVREVENT_RSP" then
			print("decode event_rsp type",decodeTable.event_rsp.type)
			print("decode event_rsp result",decodeTable.event_rsp.result)			
		elseif decodeTable.message_id=="DEV_LOCATION" then
			print("decode type",decodeTable.location.type)
			
			if decodeTable.location.gps_exist then
				print("decode gps_info longitude",decodeTable.location.gps_info.longitude)
				print("decode gps_info latitude",decodeTable.location.gps_info.latitude)
				print("decode gps_info degree",decodeTable.location.gps_info.degree)
				print("decode gps_info speed",decodeTable.location.gps_info.speed)
				print("decode gps_info viewed_sates",decodeTable.location.gps_info.viewed_sates)
			end
			
			if decodeTable.location.cell_exist then
				for k,v in ipairs(decodeTable.location.cell_info.cells) do
					if type(v)=="table" then
						print("decode cell_info",k,"lac_id",v.lac_id)
						if type(v.cells)=="table" then
							for m,n in ipairs(v.cells) do
								print("decode cell_info",k,"cells",m,n.mcc,n.mnc,n.cell_id,n.cell_rssi)
							end
						end
					end
				end
				print("decode ta",decodeTable.location.cell_info.ta)
			end
			
			if type(decodeTable.location.status)=="table" then
				print("decode status shake",decodeTable.location.status.shake)
				print("decode status charger",decodeTable.location.status.charger)
				print("decode status acc",decodeTable.location.status.acc)
				print("decode status gps",decodeTable.location.status.gps)
				print("decode status rssi",decodeTable.location.status.rssi)
				print("decode status vbat",decodeTable.location.status.vbat)
				print("decode status charge_status",decodeTable.location.status.charge_status)
			end
		end
	end
end

--[[
函数名：pack
功能  ：根据ptotobuffer协议进行封包
参数  ：
		id:协议对应指令ID
返回值：封包字符串
]]
function pack(id,...)
	if not imei then imei = bcd(get("IMEI"),8) end

	local head = schar(id)
			
	local function gpslbs1(w,typ)
		local t = get("GPS")		
		lng = enlnla(t.fix,t.lng)
		lat = enlnla(t.fix,t.lat)
		local stat = get("STATUS")
		local shk,chgstat,chg,gpstat = false,chg.getcharger(),"NOT_CHARGE",false
		if stat.shake == 1 then shk = true end
		if chgstat then chg = "CHARGING" end
		if stat.gps == 1 then gpstat = true end	
		local _,adcr = adcv.read()
		local typstr = "DEV_TIMER_REPORT"
		if typ == QRYPOS then typstr = "SVR_QUERY_RSP" end 
		local locationGPS = 
		{
			message_id = "DEV_LOCATION",
			location =
			{
				type = typstr,
				gps_exist = true,
				cell_exist = true,
				gps_info =
				{
					longitude = t.lng,
					latitude = t.lat,
					degree = t.cog,
					speed = t.spd,
					viewed_sates = get("GPSTAT").satenum,
				},
				cell_info =
				{
					cells = cellinfoext(),
					ta = get("TA"),
				},
				status =
				{
					shake = shk,
					charger = chgstat,
					acc = acc.getflag(),
					gps = gpstat,
					rssi = get("RSSI"),
					vbat = stat.volt,
					charge_status = chg,
					adc = adcr
				},
			}
		}
		encodeStr = protobuf.encode("tracker.Client",locationGPS)
		--print("locationGPSStr",string.toHex(encodeStr))
		decodeAndPrint(encodeStr)
		return encodeStr		
	end
			
	local function heart()
		local stat = get("STATUS")
		local shk,chgstat,chg,gpstat = false,chg.getcharger(),"NOT_CHARGE",false
		if stat.shake == 1 then shk = true end
		if chgstat then chg = "CHARGING" end
		if stat.gps == 1 then gpstat = true end
		local _,adcr = adcv.read()
		local devheart = 
		{
			message_id = "DEV_HEART",
			heart = 
			{
				status =
				{
					shake = shk,
					charger = chgstat,
					acc = acc.getflag(),
					gps = gpstat,
					rssi = get("RSSI"),
					vbat = stat.volt,
					charge_status = chg,
					adc = adcr,
				},
			}
		}
		local encodeStr = protobuf.encode("tracker.Client",devheart)
		print("logInStr",string.toHex(encodeStr))
		decodeAndPrint(encodeStr)
		return encodeStr
	end	
	
	local function rptpara(dat)
		local pararpt = 
		{
			message_id = "DEV_PARA_REPORT",
			para_report = 
			{
				type = "GUARD",
				guard = nvm.get("guard"),				
			}
		}
		local encodeStr = protobuf.encode("tracker.Client",pararpt)
		print("pararptStr",string.toHex(encodeStr))
		decodeAndPrint(encodeStr)		
		return encodeStr or ""
	end
	
	local function rsp(typ,result)
		local rlt = false
		if result == 1 then rlt = true end
		local pararsp = 
		{
			message_id = "DEV_SVRSETPARA_RSP",
			set_para_rsp = 
			{
				type = "GUARD",
				result = rlt,				
			}
		}
		local encodeStr = protobuf.encode("tracker.Client",pararsp)
		print("pararspStr",string.toHex(encodeStr))
		decodeAndPrint(encodeStr)
		return encodeStr		
	end
	
	local function eventrsp(typ,result)
		print("eventrsp",typ,result,schar(1))
		local rlt = false
		local ty
		if typ == QRYLOC then
			ty = "SVR_QUERY_LOCATION"
		elseif typ == RESET then
			ty = "SVR_RESET"
		elseif typ == POWEROFF then
			ty = "SVR_POWEROFF"	
		elseif typ == RESTORE then
			ty = "SVR_RESTORE"
		elseif typ == QRYPARA then	
			ty = "SVR_SET_PARA"			
		end
		if result == schar(1) then rlt = true end
		local pararsp = 
		{
			message_id = "DEV_SVREVENT_RSP",
			event_rsp = 
			{
				type = ty,
				result = rlt,				
			}
		}
		local encodeStr = protobuf.encode("tracker.Client",pararsp)
		print("pararspStr",string.toHex(encodeStr))
		decodeAndPrint(encodeStr)
		return encodeStr 		
	end
	
	local function info()
		--登录报文的封包和解包
		local logIn = 
		{
			message_id = "DEV_LOGIN",
			log_in =
			{
				project_id = get("PROJECTID"),
				project_name = get("PROJECT").."_"..rtos.get_version(),
				script_version = get("VERSION"),
				iccid = get("ICCID"),
				imsi = get("IMSI"),
				heart_interval = get("HEART"),
				power_on = poweron	
			}
		}
		local encodeStr = protobuf.encode("tracker.Client",logIn)
		print("logInStr",string.toHex(encodeStr))
		decodeAndPrint(encodeStr)
		return encodeStr
	end
	
	local procer = {
		[GPSLBS1] = gpslbs1,
		[HEART] = heart,
		[RPTPARA] = rptpara,
		[SETPARARSP] = rsp,
		[DEVEVENTRSP] = eventrsp,
		[INFO] = info,
	}	

	local s = procer[id](...)
	if id == INFO then poweron = false end
	print("pack",id,string.toHex(s),poweron)
	return s
end

--[[
函数名：unpack
功能  ：根据ptotobuffer协议进行封包
参数  ：
		s:需要解析字符串
返回值：解包字符串
]]
function unpack(s)
	local packet = {}	
	
	local function setpara(d,p)
		if slen(d) > 0 then			
			
			local function unsignshort(m)
				if slen(m) ~= 2 then return end
				_,packet.val = m
				return true
			end
			
			local function empty(m)
				return m==""
			end
																		
			local proc =
			{
				[RPTFREQ] = unsignshort,
				[GUARDON] = empty,
				[GUARDOFF] = empty,
			}
			if d == "GUARD" and p then
				packet.cmd = GUARDON	
			elseif d == "GUARD" and not p then
				packet.cmd = GUARDOFF
			elseif d == "GUARD" and p then
				packet.cmd = RPTFREQ
			end
			return proc[packet.cmd](p) and packet or nil
		end
	end
		
	local function empty(d)
		return true
	end
	
	local function prompt(d)
		if d == "" then return end
		packet.data = d
		return true
	end
			
	local function smpset(d,p)
		if slen(d) > 0 then						
			local function datetime(m)				
				--print("Server datetime m",m)				
				packet.year = ssub(m,1,4)
				packet.month = ssub(m,5,6)
				packet.day = ssub(m,7,8)
				packet.hour = ssub(m,9,10)
				packet.min = ssub(m,11,12)
				packet.sec = ssub(m,13,14)				
				return true
			end
									
			local proc =
			{
				[DATETIME] = datetime,				
			}
			
			if d == "DATE" then packet.cmd = DATETIME end
			if not proc[packet.cmd] then print("protoair.unpack:unknwon smpset",sbyte(d)) return end			
			return proc[packet.cmd](p) and packet or nil
		end
	end
	
	local procer = {
		[SETPARA] = setpara,
		[QRYLOC] = empty,
		[RESET] = empty,
		[POWEROFF] = empty,
		[RESTORE] = empty,
		[SMPSET] = smpset,
	}
	
	local decodeTable = protobuf.decode("tracker.Server",s)
	print("Server decodeTable")
	local decodestr,typ,id
	if decodeTable then
		print("Server decode message_id",decodeTable.message_id,type(decodeTable.message_id))
		if decodeTable.message_id=="SVR_SET_PARA" then
			id = SETPARA
			typ = decodeTable.svr_set_para.type
			print("decode type",typ)
			if typ == "GUARD" then				
				decodestr = decodeTable.svr_set_para.guard
				print("decode decodestr",decodestr)	
			elseif typ == "LOCATION_INTERVAL" then
				decodestr = decodeTable.svr_set_para.location_interval
				print("decode decodestr",decodeTable.svr_set_para.location_interval)	
			end
		elseif decodeTable.message_id=="SVR_QUERY_LOCATION" then
			id = QRYLOC
		elseif decodeTable.message_id=="SVR_RESET" then
			id = RESET
		elseif decodeTable.message_id=="SVR_POWEROFF" then
			id = POWEROFF		
		elseif decodeTable.message_id=="SVR_RESTORE" then
			id = RESTORE
		elseif decodeTable.message_id=="SVR_SET_DATE" then
			id = SMPSET
			typ = decodeTable.svr_set_para.type
			print("Server decode type",typ)
			if decodeTable.svr_set_para.type == "DATE" then
				decodestr = decodeTable.svr_set_para.date
				print("Server decode decodestr",decodestr)
			end
		end
	end	
	print("Server decode id",id,typ,decodestr)
	packet.id = id
	return procer[id](typ,decodestr) and packet or nil				
end

function reget(id)
	get = id
end
--此文件沒用

